package idea.iqtitles.explorer;


import idea.de.explorer.IDataElemsTopComponent;
import idea.icci.explorer.CustomContentItemsTopComponent;
import idea.ipreview.IPreviewTopComponent;
import idea.model.dbmanager.QueryManager;
import idea.model.dbmanager.SfoEnum;
import idea.model.qnrlibrary.CustomContentItems;
import idea.model.qnrlibrary.QuestionTitle;
import idea.model.qnrlibrary.QuestionTitleLinkView;
import idea.sfo.utils.SfoDocumentManager;
import idea.sfo.viewer.SfoViewerTopComponent;
import java.awt.event.ActionEvent;
import java.beans.IntrospectionException;
import java.util.Iterator;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import static javax.swing.Action.NAME;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import javax.xml.bind.JAXBException;
import org.apache.commons.codec.binary.Base64;
import org.openide.nodes.BeanNode;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.lookup.Lookups;
import org.openide.windows.WindowManager;
import org.openide.windows.IOProvider;
import org.openide.windows.InputOutput;
import sfoutils.SFOUtils;


/**
 * Child Factory class for Question Title nodes in IQuestionTitleTopComponent.
 *
 * @author rejun001
 */
public class IQuestionTitleChildFactory extends ChildFactory<QuestionTitleLinkView>
{

    /**
     * The result list of Question Titles
     */
    private List<QuestionTitleLinkView> resultList;

    /**
     * The Tree top component that displays the SFO items
     */
    private SfoViewerTopComponent treeTC = null;
    /**
     * The preview top component that displays the Jpeg preview
     */
    private IPreviewTopComponent prevTC = null;
    /**
     * Allows writing to the Output window.
     */
    InputOutput io = null;
    /**
     * The CCI Top Component for drill down.
     */
    CustomContentItemsTopComponent ccitc = null;


    /**
     * Class Constructor for IQuestionTitleFactory
     *
     * @param resultList -- the list of QuestionTitleLinkView objects containing
     * the table data
     */
    public IQuestionTitleChildFactory( List<QuestionTitleLinkView> resultList )
    {
        this.resultList = resultList;
        io = IOProvider.getDefault().getIO( "Designer SFO" , true );
        ccitc = (CustomContentItemsTopComponent) WindowManager.getDefault().findTopComponent( "CustomContentItemsTopComponent" );
    }


    /**
     * This adds the rows from the results list to the list of node items.
     *
     * @param list -- the list of node items, to be populated from result list
     * @return always true
     */
    @Override
    protected boolean createKeys( List<QuestionTitleLinkView> list )
    {
        for ( QuestionTitleLinkView qtlv : resultList )
        {
            list.add( qtlv );
        }
        return true;
    }


    /**
     * This creates a node for each single key
     *
     * @param key -- the bean (key) the node will contain
     * @return the new Node
     */
    @Override
    protected Node createNodeForKey( QuestionTitleLinkView key )
    {
        IQuestionTitleNode node = null;
        try
        {
            node = new IQuestionTitleNode( key );
        }
        catch ( Exception e )
        {
            Exceptions.printStackTrace( e );
        }
        return node;
    }


    /**
     * inner class:
     * subclass of BeanNode that sets up an individual Question Title Node
     * @see BeanNode
     */
    private class IQuestionTitleNode extends BeanNode
    {

        /**
         * The quest ID to drill down to CCI level
         */
        private String currQuestId = null;  // used to drill down
        /**
         * The current Category (for the display label)
         */
        private String currCategory = null;
        /**
         * The current sequence number (for the display label)
         */
        private String currSeq = null;
        /**
         * There are two non-ASCII chars at the beginning of an SFO clob string.
         * Microsoft big- vs.-little-endian flags.
         */
        final int BAD_CHAR_COUNT = 2;       // number of non ASCII chars at start of SFO (this is big-endian vs. little-endian stuff)
        /**
         * The actual non-ASCII chars themselves
         */
        char[] savedNonAscii = new char[ BAD_CHAR_COUNT ];


        /**
         * Class Constructor
         *
         * @param bean - a QuestionTitleLinkView, which contains the displayable info for a QuestionTitle row
         * @throws IntrospectionException
         */
        public IQuestionTitleNode( QuestionTitleLinkView bean ) throws IntrospectionException
        {
            super( bean , Children.LEAF , Lookups.singleton( bean ) );
            currQuestId = bean.getQuestId();
            currCategory = bean.getCategory();
            currSeq = bean.getQuestSequence().toString();

            // These show on the bottom of the Properties window.
            setDisplayName( bean.getQuestSequence().toString() );
            setShortDescription( bean.getCategory() + "<br\\>" + bean.getTitle() );

        }


        /**
         * gets context menu Actions. On the Node column, the original Action is pulling up a
         * properties window. On a field column, the original Action is a WHERE clause
         * choice. The other two (Designer and DrillDown) are added here.
         *
         * @param bln -- bln tells whether or not it's a context menu.
         * @return the array of actions for the Node.
         */
        @Override
        public Action[] getActions( boolean bln )
        {
            Action[] oldActions = super.getActions( bln );

            Action[] newActions = new Action[ oldActions.length + 3 ];
            System.arraycopy( oldActions , 0 , newActions , 0 , oldActions.length );

            /**
             * Add the new actions one by one to the Actions array
             */
            newActions[oldActions.length] = new DrillDownAction();
            newActions[oldActions.length + 1] = new ShowDesignerAction();
            newActions[oldActions.length + 2] = new ShowPreviewAction();

            return newActions;

        }


        /**
         * overrides the double-click action to drill down instead of popping up a
         * Properties window
         *
         * @return an Action
         */
        @Override
        public Action getPreferredAction()
        {
            Action a = super.getPreferredAction();
            System.out.println( "Preferred Action!! " + a.toString() + ", cat is " + currCategory + ", quest_id is " + currQuestId );

            drillDown();

            return null; // i.e. don't do the default, which is Show Propertues
        }


        /**
         * Brings up the SFO tree view window for the given quest ID. For now it
         * just uses a single test SFO file.
         *
         * @param questId -- the requested Question ID for an SFO
         */
        private void displaySFOTree( String questId )
        {
            String sfo = getSFOString( SfoEnum.DESIGNER , questId );
            String encapsulatedSfo = getSFOString( SfoEnum.ENCAPSULATED , questId );
            String layoutSfo = getSFOString( SfoEnum.LAYOUT , questId );


            if ( treeTC == null )
            {
                treeTC = (SfoViewerTopComponent) WindowManager.getDefault().findTopComponent( "SfoViewerTopComponent" );
            }
            // now this is a separate context menu item.
//            if ( prevTC == null )
//            {
//                prevTC = (IPreviewTopComponent) WindowManager.getDefault().findTopComponent( "IPreviewTopComponent" );
//            }

            try
            {
                SfoDocumentManager sdm = new SfoDocumentManager( sfo , questId , savedNonAscii );
                treeTC.displaySFO( sdm );
                //prevTC.displayPreview( sdm );
            }
            catch ( JAXBException ex )
            {
                Exceptions.printStackTrace( ex );
            }

            treeTC.open();
            treeTC.requestActive();
            //prevTC.open();
        }


        /**
         * Gets the SFO xml file for the given quest ID.
         *
         * @param questId -- the Question id whose SFO we are requesting
         * @return a String which is the requested SFO
         */
        private String getSFOString( SfoEnum sfoType , String questId )
        {
            // to the output window
            io.getOut().println( "Hello from stdout" );
            io.getErr().println( "Hello from stderr" );

            final int ARRAY_SIZE = 40000;  // this number works for most SFOs
            String sfo = null;

            // go get the Clob from the DB
            QueryManager qm = new QueryManager();
            List<QuestionTitle> qts = QueryManager.getQT( questId );
            QuestionTitle qt = qts.get( 0 ); // there should only be one
            long clen = qm.getClobFromDB( sfoType , qt );
            if ( clen > 0 )
            {
                String clobStr = qm.getClobStr();
                System.out.println( "Clob length is " + clen + ", Clob is " + clobStr.substring( 0 , 50 ) );

                // decode it
                SFOUtils su = new SFOUtils();
                su.setB64( new Base64() );
                su.setDecoded( su.getB64().decode( clobStr ) );
                String decodedStr = new String( su.getDecoded() );
                System.out.println( sfoType.toString() + " decoded compressed len " + decodedStr.length() );

                // uncompress it
                int reslen = su.inflate( su.getDecoded() , ARRAY_SIZE ); // result is put into su.output
                sfo = su.getOutput();
                int len = sfo.length();
                System.out.println( "SFO string length is " + len );

                // REMOVE and save off the two nonASCII characters at the beginning of the sfo string, and be sure two bogus chars aren't added at the end.
                char[] sfoChars = sfo.toCharArray();
                char[] sfoOut = new char[ sfoChars.length - BAD_CHAR_COUNT ];
                int j = 0;
                for ( int i = 0; i < sfoChars.length; i++ )
                {
                    if ( i < BAD_CHAR_COUNT )
                    {
                        savedNonAscii[i] = sfoChars[i];
                        continue;
                    }
                    char c = sfoChars[i];
                    sfoOut[j++] = c;
                }

                sfo = new String( sfoOut );

                System.out.println( "--------------------------------------" );
                System.out.println( sfoType.toString() + "\n/" + su.homelyPrint( sfo ) + "/" );

                // print sfo in a tab in the Output window
                io.getOut().print( su.homelyPrint( sfo ) );
            }
            else
            {
                io.getErr().print( "ERROR: No " + sfoType.name() + " in custom_content_layouts for given Question Title ID." );
            }

            io.getOut().close();
            io.getErr().close();

            return sfo;
        }


        /**
         * closes the Data Elements window
         * 
         * Changing the selected Question Title will automatically populate its
         * child, the CCI window, but there is no way of determining what the
         * Data Elements window would be, so close it.
         */
        private void closeQTChildWindows()
        {
            IDataElemsTopComponent detc = (IDataElemsTopComponent) WindowManager.getDefault()
                    .findTopComponent( "IDataElemsTopComponent" );
            detc.close();
        }


        /**
         * This brings up a table of the next level (Custom Content Items) for
         * the selected Node
         */
        private void drillDown()
        {
            /**
             * Two queries are needed since the entity file for CCIs treats a
             * questID as a QuestionTitle object rather than a String.
             */
            List<QuestionTitle> qts = QueryManager.getQT( currQuestId ); // there should only be one
            List<CustomContentItems> ccis = QueryManager.getCustomContentItems( qts.get( 0 ) );

            closeQTChildWindows();

            // display drilldown window
            ccitc.displayCCIs( ccis , currSeq , currCategory );
            ccitc.open();
            ccitc.requestActive();
        }


        /**
         * NOT USED
         */
        private class ShowPropsAction extends AbstractAction
        {

            public ShowPropsAction()
            {
                putValue( NAME , "Properties" );
            }


            @Override
            public void actionPerformed( ActionEvent e )
            {
                QuestionTitleLinkView obj = getLookup().lookup( QuestionTitleLinkView.class );
                JOptionPane.showMessageDialog( null , "Hidey Ho: " + obj.getCategory() );
            }

        }


        /**
         * inner class that creates an Action
         * 
         * The Action for drilling down: will show up on the context menu for
         * the node
         */
        private class DrillDownAction extends AbstractAction
        {

            /**
             * Constructor. The label for the context menu item.
             */
            public DrillDownAction()
            {
                putValue( NAME , "Drill Down" );
            }


            /**
             * calls the action.
             *
             * @param e ActionEvent
             */
            @Override
            public void actionPerformed( ActionEvent e )
            {
                drillDown();
            }

        }


        /**
         * The right-click context menu Action for bringing up the SFO window
         * ("Show Designer")
         */
        private class ShowDesignerAction extends AbstractAction //implements Presenter.Popup
        {

            /**
             * constructor. The label for the context menu item.
             */
            public ShowDesignerAction()
            {
                putValue( NAME , "Show Designer" );
            }


            /**
             * calls the action.
             *
             * @param e Action Event
             */
            @Override
            public void actionPerformed( ActionEvent e )
            {
                //io.setOutputVisible( false);
                //io.closeInputOutput();  // close the output window
                closeQTChildWindows();
                if ( ccitc != null && ccitc.isOpened() )
                {
                    ccitc.close();
                }

                displaySFOTree( currQuestId );
            }

        }


        /**
         * The Action for bringing up the Preview (the Jpeg)
         */
        private class ShowPreviewAction extends AbstractAction //implements Presenter.Popup
        {

            /**
             * xtor. The label for the context menu item.
             */
            public ShowPreviewAction()
            {
                putValue( NAME , "Show Preview" );
            }


            /**
             * the action performed.
             *
             * @param e ActionEvent
             */
            @Override
            public void actionPerformed( ActionEvent e )
            {
                ImageIcon jpeg = getJpeg( currQuestId );
                
                IPreviewTopComponent prevtc = (IPreviewTopComponent) WindowManager.getDefault()
                        .findTopComponent( "IPreviewTopComponent" );
                prevtc.displayPreview( jpeg , "My Preview" );
                prevtc.open();
                prevtc.requestActive();
            }


            /**
             * gets the Jpeg for this question
             *
             * @param questID
             * @return the ImageIcon for the jpeg
             */
            public ImageIcon getJpeg( String questID )
            {
                ImageIcon image;
                List<QuestionTitle> qts = QueryManager.getQT( questID );
                QuestionTitle qt = null;
                Iterator<QuestionTitle> qtit = qts.iterator();
                while ( qtit.hasNext() )
                {
                    qt = qtit.next();
                    break;               // there is only one
                }
                image = QueryManager.getJpegFromDB( qt );

                return image;
            }

        }

    }

}
